// LuoDnd.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"

#include "./include/LuoDnDAssist.h"

#include "./helper/UThreading.h"
#include "./helper/UException.h"

#include "./dnd/DataObject.h"
#include "./dnd/DropSource.h"
#include "./dnd/DropTarget.h"
#include "./dnd/ComObjects.h"
#include "./dnd/DnDUtils.h"

#include "DropTargetImpl.h"

HINSTANCE g_hDLL;

#ifdef _MANAGED
#pragma managed(push, off)
#endif

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call) {
	case DLL_PROCESS_ATTACH:
		g_hDLL = hModule;
		DisableThreadLibraryCalls(hModule);
		return TRUE;

	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}

    return TRUE;
}


/* */
// iAModule
class iAModule :
	public ATL::CAtlDllModuleT<iAModule> {
};

// iAModule
iAModule _AtlModule;

/* */
void*  UActivator_Create();

extern "C" HRESULT LUODNDASSIST_FUNC STDCALL LuoDnDAssist_Activate(LPCWSTR token, void ** obj)
{
	if (obj == 0) {
		return E_INVALIDARG;
	}

	*obj = 0;

	if (token == 0) {
		return E_INVALIDARG;
	}

	void*  new_obj = 0;

	if (lstrcmpW(token, L"activator") == 0) {
		new_obj = UActivator_Create();
	}

	if (new_obj == 0) {
		return E_NOINTERFACE;
	}

	*obj = new_obj;
	return S_OK;
}

/* */
using LuoDnDAssist:: DataItem;
using LuoDnDAssist:: DropParam;
using LuoDnDAssist:: DropCallback_t;

// ActivatorImpl
class ActivatorImpl :
	public LuoDnDAssist::UActivator,
	public LuoUtils::URunnable {
		LuoUtils::Thread m_Thread;

		LuoUtils::CritSec m_CSec;
public:
	ActivatorImpl()
	{
	}

	/* */
	virtual HRESULT STDCALL Initialize()
	{
		//LuoUtils::Environment::Initialize();
		try {
			m_Thread.SetupName("LuoDnDAssist");

			m_Thread.Create(this);
		}
		catch(LuoUtils::Exception &) {
			return E_FAIL;
		}

		return S_OK;
	}

	virtual void STDCALL Uninitialize()
	{
		DWORD tid = m_Thread.GetThreadId();
		for (int i = 0; i < 3; i++) {
			if (i > 0) {
				::Sleep(100);
			}

			if (PostThreadMessage(tid, WM_QUIT, 0, 0)) {
				break;
			}
		}

		m_Thread.Join();
		m_Thread.Close();
	}

	/* DropSource */
	virtual HRESULT CreateDataObject(const DataItem* items, int count, IDataObject ** pp)
	{
		if (items == 0 || pp == 0) {
			return E_INVALIDARG;
		}

		*pp = 0;

		ATL::CComPtr<IDataObject> dobj;
		HRESULT hRslt = DragDrop::DataObjectImpl::Activate(&dobj);
		if (FAILED(hRslt)) {
			return hRslt;
		}

		for (int i = 0; i < count; i++) {
			const DataItem&	 item = items[i];
			hRslt = DragDrop::Utils::SetData(dobj, item.Format, item.Data, item.Size);
			if (FAILED(hRslt)) {
				return hRslt;
			}
		}

		*pp = dobj.Detach();

		return S_OK;
	}

	virtual HRESULT CreateDropSource(IDropSource ** pp)
	{
		if (pp == 0) {
			return E_INVALIDARG;
		}

		*pp = 0;

		ATL::CComPtr<IDropSource> dsrc;

		HRESULT hRslt = DragDrop::DropSourceImpl::Activate(&dsrc);
		if (FAILED(hRslt)) {
			return hRslt;
		}

		*pp = dsrc.Detach();

		return S_OK;
	}

	/* DropTarget */
	virtual HRESULT CreateDropTarget(void* context, DropCallback_t callback, IDropTarget ** pp)
	{
		if (pp == 0) {
			return E_INVALIDARG;
		}

		*pp = 0;

		ATL::CComPtr<IDropTarget> dtrg;

		HRESULT hRslt = DropTargetImpl::Activate(context, callback, &dtrg);
		if (FAILED(hRslt)) {
			return hRslt;
		}

		*pp = dtrg.Detach();

		return S_OK;
	}

	/* */
	virtual int STDCALL Run()
	{
		HRESULT hRslt = ::OleInitialize(0);
		if (FAILED(hRslt)) {
			return -1;
		}

		int result = DoProcess();

		::OleUninitialize();

		return result;
	}

	int DoProcess()
	{
		int result = -1;

		for (;;) {
			MSG msg = { 0};

			BOOL fPeek = ::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE);
			if (fPeek) {
				if (msg.message == WM_QUIT) {
					result = (int)msg.wParam;
					break;
				}

				::TranslateMessage(&msg);
				::DispatchMessageW(&msg);
			}
			else {
				WaitMessage();
			}
		}

		return result;
	}

	/* */
};

// ActivatorImpl
void* UActivator_Create()
{
	static ActivatorImpl impl;
	return static_cast<LuoDnDAssist::UActivator*>(&impl);
}

/* */

#ifdef _MANAGED
#pragma managed(pop)
#endif

